/**
* @file op_compile_processor.cpp
*
* Copyright (C) Huawei Technologies Co., Ltd. 2019-2020. All Rights Reserved.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include "op_compile_processor.h"
#include "runtime/dev.h"
#include "model/acl_resource_manager.h"
#include "op_compile_service.h"
#include "ge/ge_api.h"
#include "error_codes_inner.h"
#include "toolchain/profiling_manager.h"

namespace acl {
namespace {
    std::string JIT_COMPILE = "ge.jit_compile";
    aclError SetJitCompileDefaultValue()
    {
        std::map<std::string, std::string> options;
        acl::OpCompileProcessor::GetInstance().GetGlobalCompileOpts(options);
        if (options.find(JIT_COMPILE) != options.cend()) {
            return ACL_SUCCESS;
        }
        static std::string jitCompileDefaultValue {};
        if (jitCompileDefaultValue.empty()) {
            const auto length = aclGetCompileoptSize(ACL_OP_JIT_COMPILE);
            auto var = std::unique_ptr<char[]>(new (std::nothrow) char[length]);
            ACL_CHECK_MALLOC_RESULT(var);
            ACL_REQUIRES_OK(aclGetCompileopt(ACL_OP_JIT_COMPILE, var.get(), length));
            jitCompileDefaultValue = std::string(var.get()) == "enable" ? "1" : "0";
            ACL_LOG_INFO("ge.jit_compile option does not exist, get default value: %s,",
                         jitCompileDefaultValue.c_str());
        }

        if (jitCompileDefaultValue == "0") {
            acl::OpCompileProcessor::GetInstance().SetJitCompileFlag(0);
            acl::OpCompileProcessor::GetInstance().SetCompileOpt(JIT_COMPILE, jitCompileDefaultValue);
            std::string optKey = "ge.shape_generalized";
            std::string v = "1";
            acl::OpCompileProcessor::GetInstance().SetCompileOpt(optKey, v);
        }
        return ACL_SUCCESS;
    }
}
OpCompileProcessor::OpCompileProcessor()
{
    (void)Init();
}

OpCompileProcessor::~OpCompileProcessor()
{
}

aclError OpCompileProcessor::Init()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (isInit_) {
        return ACL_SUCCESS;
    }
    aclError ret = SetOption();
    if (ret != ACL_SUCCESS) {
        ACL_LOG_INNER_ERROR("[Set][Options]OpCompileProcessor init failed!");
        return ret;
    }

    isInit_ = true;
    return ACL_SUCCESS;
}

aclError OpCompileProcessor::SetOption()
{
    char socVersion[SOC_VERSION_LEN] = {0};
    auto ret = rtGetSocVersion(socVersion, sizeof(socVersion));
    if (ret != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("[Get][SocVersion]get soc version failed, runtime result = %d",
            static_cast<int32_t>(ret));
        return ACL_GET_ERRCODE_RTS(ret);
    }

    std::map<std::string, std::string> options = {
        { ge::SOC_VERSION, std::string(socVersion) },
        { ge::OP_SELECT_IMPL_MODE, std::string("high_precision")}
    };
    return OpCompileService::GetInstance().SetCompileStrategy(NATIVE_COMPILER, options);
}

aclError OpCompileProcessor::OpCompile(AclOp &aclOp)
{
    ACL_PROFILING_REG(acl::AclProfType::OpCompile);
    ACL_REQUIRES_TRUE(isInit_, ACL_ERROR_FAILURE, "[Init][Env]init env failed!");
    ACL_REQUIRES_OK(SetJitCompileDefaultValue());
    return AclResourceManager::GetInstance().GetOpModel(aclOp);
}

aclError OpCompileProcessor::OpCompileAndDump(AclOp &aclOp, const char_t *const graphDumpPath,
    const aclGraphDumpOption *const dumpOpt) const
{
    ACL_PROFILING_REG(acl::AclProfType::OpCompileAndDump);
    ACL_REQUIRES_TRUE(isInit_, ACL_ERROR_FAILURE, "[Init][Env]init env failed!");
    ACL_REQUIRES_OK(SetJitCompileDefaultValue());
    return OpCompileService::GetInstance().CompileAndDumpGraph(aclOp, graphDumpPath, dumpOpt);
}

aclError OpCompileProcessor::SetCompileOpt(std::string &opt, std::string &value)
{
    ACL_REQUIRES_TRUE(isInit_, ACL_ERROR_FAILURE, "[Init][Env]init env failed!");
    std::lock_guard<std::mutex> lock(globalCompileOptsMutex);
    globalCompileOpts[opt] = value;
    return ACL_SUCCESS;
}

aclError OpCompileProcessor::GetCompileOpt(const std::string &opt, std::string &value)
{
    ACL_REQUIRES_TRUE(isInit_, ACL_ERROR_FAILURE, "[Init][Env]init env failed!");
    std::lock_guard<std::mutex> lock(globalCompileOptsMutex);
    auto iter = globalCompileOpts.find(opt);
    if (iter == globalCompileOpts.end()) {
        return ACL_ERROR_INVALID_PARAM;
    }
    value = iter->second;
    return ACL_SUCCESS;
}

aclError OpCompileProcessor::GetGlobalCompileOpts(std::map<std::string, std::string> &currentOptions)
{
    ACL_REQUIRES_TRUE(isInit_, ACL_ERROR_FAILURE, "[Init][Env]init env failed!");
    currentOptions.clear();
    std::lock_guard<std::mutex> lock(globalCompileOptsMutex);
    currentOptions.insert(globalCompileOpts.cbegin(), globalCompileOpts.cend());
    return ACL_SUCCESS;
}

aclError OpCompileProcessor::SetCompileFlag(int32_t flag) const
{
    ACL_REQUIRES_TRUE(isInit_, ACL_ERROR_FAILURE, "[Init][Env]init env failed!");
    AclResourceManager::GetInstance().SetCompileFlag(flag);
    return ACL_SUCCESS;
}

aclError OpCompileProcessor::SetJitCompileFlag(int32_t flag) const
{
    ACL_REQUIRES_TRUE(isInit_, ACL_ERROR_FAILURE, "[Init][Env]init env failed!");
    AclResourceManager::GetInstance().SetJitCompileFlag(flag);
    return ACL_SUCCESS;
}
} // namespace acl

